const FUN = require('../comm/func');
const rCache = require('../model/redis_cache');
const timLib = require('../comm/time-lib');
const dataCalc = require('../comm/data-calc');
const dbPcyData = require('../model/model_data');
const dbHelp = require('../comm/db-helper');
const mbLib = require('../comm/modbus-lib');
const PMR = require('../utils/policyManger');

module.exports = class MeterAlarm
{
  //将数据库选项缓存到redis
  //policy 来自数据库的记录
  //opts 数据库json字段的解析对象
  //CacheCallback 存储执行函数
  static InitCache(policy,opts,CacheCallback)
  {
    let obj=PMR.baseParam(policy);//即将缓存到redis的参数

    //json options参数存储
    obj.data_width = opts.data_width;//数据宽度
    obj.out = '';
    obj.calc = opts.calc;//输出表达式
    for(let i=0;i<opts.calc.split(',').length;i++){obj.out+=i+',';}
    obj.only_once = opts.only_once;//是否只报警一次 true,false
    obj.msg = opts.msg;
    obj.enable = opts.enable;//屏蔽开关 true,false

    //duration必须和calc同长
    let duration = opts.duration.split(',');//触发时长
    for(let i=0;i<obj.calc.split(',').length;i++)
    if(duration[i]==null) duration[i]=duration[0];
    obj.duration=duration.join(',');

    //alarm_gap必须和calc同长
    let alarm_gap = opts.alarm_gap.split(',');//连续报警间隔
    for(let i=0;i<obj.calc.split(',').length;i++)
    if(alarm_gap[i]==null) alarm_gap[i]=alarm_gap[0];
    obj.alarm_gap=alarm_gap.join(',');

    //数据库处理
    dbHelp.createTab(obj.table,policy.uid);

    //缓存主站参数
    obj.hwid = policy.master;
    obj.role = 'master';
    obj.brother = policy.brother;
    CacheCallback(policy.id,policy.master,obj);//记录到缓存
    rCache.delNode(PMR.rdNode(obj));//删除之前的历史记录

    //缓存从站参数
    if(Number(policy.brother) && policy.brother!=-1)
    {
      obj.hwid = policy.brother;
      obj.role = 'slave';
      obj.brother = policy.master;
      CacheCallback(policy.id,obj.hwid,obj);//记录到缓存
      rCache.delNode(PMR.rdNode(obj));//删除之前的历史记录
    }
  }//end InitCache


  //modbus数据总分析
  // topic = {project,devType,func,devId}
  // dConf即InitCache中的obj
  // dataBuf即mq消息内容，buf对象
  static async DataIn(topic, dConf, dataBuf)
  {
    mbLib.mbPacketParse(topic.func, dConf, dataBuf, (last,sConf)=>{
      this.dataHandler(last,sConf,dataBuf,timLib.NowStamp());
    })
  }//end function ModbusTx


  //普通的数据处理,每隔一段时间就求平均值入数据库
  static dataHandler(last,dConf,data,stamp)
  {
    //记数值
    let save=[];
    let regValArray={};
    let data_fmt=dConf.data_width.split(':');//分割数据宽度和数据格式
    for(let i=0;i<last.regNum;i+=(data_fmt[0]/2))
    {
      let reg=Number(last.regStart)+i;
      let regVal=(Number(data[3+i*2])<<8)+Number(data[4+i*2]);

      if(data_fmt[0]==4)//不同字节长度得的处理
        regVal=(regVal<<16)+(Number(data[5+i*2])<<8)+Number(data[6+i*2]);

      if(data_fmt[1]=='f') regVal=FUN.ieee745_toFloat(regVal).toFixed(4);
      save.push({reg:reg,value:regVal,name:'#'+last.addr+':'+reg});//加入到数组
      regValArray['#'+last.addr+':'+reg]=regVal;//构建对象，暂存数据
    }

    //存储寄存器值
    rCache.setKey(PMR.rdNode(dConf),'regVal',JSON.stringify(regValArray),86400);

    //pcyLog('cache',save);
    dataCalc.saveOutVar(dConf,save,last.addr,stamp,(mode,fields,dArray)=>{
      if(mode=='var')
      {
        let info=[];
        fields=fields.split(',');
        for(let i=0;i<fields.length;i++) info[fields[i]]=dArray[i];//组合对象，方便处理

        this.infoJudge(dConf,info,stamp);
      }//end if mode==var
    })//end saveoutvar

    //pcyLog('cache',nodeDevAddr,newData);
  }//end funciton

  //处理单条信息的运算结果
  //info['vtag']存放序号
  //info['value']表达式结果0,1
  static infoJudge(dConf,info,stamp)
  {
    let nodeDevStamp=PMR.rdNode(dConf);

    //pcyLog('alarm',dConf.policy_id,dConf.hwid,info,info['value']?'true':'false');
    if(info['value']==1)//表达式为真
    {
      let duration=Number(dConf.duration.split(',')[info['vtag']]);
      let alarm_gap=Number(dConf.alarm_gap.split(',')[info['vtag']]);
      let exp=duration<3600?3600:duration;//redis最少缓存一小时

      rCache.getKey(nodeDevStamp,info['vtag']).then((lastStamp)=>{//lastStamp存储的是上一次满足条件的时间
        if(lastStamp==null)//第一次满足要求
        {
          pcyLog('[%d][%d] first alarm calc[%d]', dConf.policy_id, dConf.hwid, info['vtag'], timLib.NowStr());
          if(duration==0)//即时触发
          {
            rCache.setKey(nodeDevStamp,info['vtag'],stamp+alarm_gap*1000,exp);
            this.alarmHandler(0,dConf,info,1001);
          }
          else//持续一段时间才触发
          {
            rCache.setKey(nodeDevStamp,info['vtag'],stamp,exp);
          }
        }
        else//之前满足过要求
        {
          let gap=(stamp-lastStamp)/1000;

          if(gap<duration)//持续时间不够长
          {
            rCache.setKey(nodeDevStamp,info['vtag'],lastStamp,exp);//刷新exprise
          }
          else//满足了要求，并且持续时间够长
          {
            rCache.setKey(nodeDevStamp,info['vtag'],stamp+alarm_gap*1000-duration*1000,exp);//刷新exprise
            this.alarmHandler(gap,dConf,info,1003);
          }
        }
      })
    }
    else//不满足要求
    {
      rCache.del(nodeDevStamp,info['vtag']);
    }
  }

  //报警处理
  //支持#sn替换序号
  //支持#1:3替换变量
  //支持${405*10}替换表达式
  static alarmHandler(gap,dConf,info,code=1000)
  {
    if(dConf.enable!='true') return;
    if(dConf.only_once=='true')//单次报警模式
    {
      rCache.set(PMR.policyNode(dConf),'',{'enable':'false'});
      rCache.del(PMR.rdNode(dConf),info['vtag']);
    }

    let stamp=timLib.NowStamp();
    let msg=dConf.msg.split('|')[info['vtag']];

    //处理消息变量替换
    msg=msg?msg:dConf.msg;
    msg=msg.replace(/#sn/g,Number(info['vtag'])+1);

    //找到缓存的寄存器值
    rCache.getKey(PMR.rdNode(dConf),'regVal').then((regValStr)=>{
      if(!regValStr) return;
      let regVal=JSON.parse(regValStr);

      //先替换寄存器的值
      let regs=msg.match(/#[0-9]+:[0-9]+/g);//找到类似#3:0这样的寄存器
      if(regs)
      for(let regId of regs){
        let findVal = regVal[regId];
        if(findVal){
          let regStr=new RegExp(regId,'g');
          msg=msg.replace(regStr,findVal);
        }
      }

      //替换算式的运算结果
      let calc=msg.match(/\${.+?}/g);//找到类似${405*10}这样的算式
      if(calc)
      for(let c of calc){
        let regex=/\${(.+?)}/g;
        let cVal=eval(regex.exec(c)[1]);//找出${}里的内容，并执行
        if(cVal!=null) msg=msg.replace(c,cVal);
      }

      //发出报警消息
      //pcyLog('msg',dConf.policy_id,msg);
      pcyLog('[%d][%d] %ds alram calc[%d]', dConf.policy_id, dConf.hwid, gap, info['vtag'], timLib.NowStr(), code);
      dbPcyData.addAlaramLog(dConf.uid, dConf.hwid, dConf.policy_id, info['vtag'], msg, stamp).then((id)=>{
        PMR.alarmReport(id,dConf);
      })

      //删除数据库中冗余的前期数据
      dataCalc.dbRecordTidy(dConf.uid,dConf.table,dConf.policy_id,dConf.record_limit);
      //stamp-=30*86400*1000;//只保存一定时间的alarm信息
      //dbAlarm.cleanByStamp(dConf.uid, dConf.policy_id, stamp);
    })//end getKey regVal
  }//end function

} //end class Modbus